import os
import json
import pytest
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
from datetime import datetime

# 邮件配置参数
SMTP_SERVER = 'smtp.qq.com'  # SMTP服务器地址
SMTP_PORT = 587  # SMTP服务器端口（TLS）
EMAIL_FROM = '394522655@qq.com'  # 发件人邮箱地址
EMAIL_TO = '394522655@qq.com'  # 收件人邮箱地址
EMAIL_SUBJECT = 'Test Report'  # 邮件主题
EMAIL_USERNAME = '394522655@qq.com'  # 登录SMTP服务器的用户名
EMAIL_PASSWORD = 'xbokykrlnmovbhch'  # QQ邮箱的授权码


def get_docstring_from_nodeid(nodeid):
    """
    根据 nodeid 获取对应的测试函数的文档字符串。

    :param nodeid: 测试用例的 nodeid
    :return: 文档字符串内容或空字符串
    """
    try:
        parts = nodeid.split('::')
        # 将文件路径转换为模块名称
        module_path = parts[0].replace('.py', '').replace('/', '.').replace('\\', '.')
        class_name = parts[1] if len(parts) > 1 else None
        function_name = parts[-1]

        # 动态导入模块
        module = __import__(module_path, fromlist=[class_name])

        # 获取类和方法对象
        if class_name:
            cls = getattr(module, class_name)
            func = getattr(cls, function_name)
        else:
            func = getattr(module, function_name)

        # 获取文档字符串
        raw_docstring = func.__doc__
        if raw_docstring:
            lines = raw_docstring.strip().split('\n')
            stripped_lines = [line.lstrip() for line in lines]
            return '\n'.join(stripped_lines)
        else:
            return ""
    except ImportError as e:
        print(f"无法导入模块: {e}")
        return ""
    except AttributeError as e:
        print(f"无法找到属性: {e}")
        return ""
    except Exception as e:
        print(f"无法获取文档字符串: {e}")
        return ""


def generate_html_report(reports_dir, report_filename, test_cases):
    """
    生成 HTML 报告文件。

    :param reports_dir: 报告存储目录
    :param report_filename: 报告文件名
    :param test_cases: 测试用例列表
    :return: 报告文件路径
    """
    report_path = os.path.join(reports_dir, report_filename)
    with open(report_path, 'w', encoding='utf-8') as file:
        file.write("<html>\n")
        file.write("  <head>\n")
        file.write("    <title>自动化测试报告</title>\n")  # 添加标题标签
        file.write("  </head>\n")
        file.write("  <body>\n")
        file.write("    <h1>食堂 班牌 执行报告</h1>\n")  # 添加指定内容
        file.write("    <table border='1'>\n")
        file.write("      <tr><th>执行的用例ID</th><th>用例描述</th><th>通过/失败</th><th>备注</th></tr>\n")
        for case in test_cases:
            file.write(f"      <tr><td>{case['id']}</td><td>{case['description']}</td><td>{case['status']}</td><td>{case['comment']}</td></tr>\n")
        file.write("    </table>\n")
        file.write("  </body>\n")
        file.write("</html>\n")
    return report_path


def send_email_with_attachment(file_path, smtp_server, smtp_port, email_from, email_to, email_subject, email_username,
                               email_password):
    """
    发送带有附件的电子邮件。

    :param file_path: 附件文件路径
    :param smtp_server: SMTP服务器地址
    :param smtp_port: SMTP服务器端口
    :param email_from: 发件人邮箱地址
    :param email_to: 收件人邮箱地址
    :param email_subject: 邮件主题
    :param email_username: 登录SMTP服务器的用户名
    :param email_password: 登录SMTP服务器的密码
    """
    try:
        # 创建邮件对象
        msg = MIMEMultipart()
        msg['From'] = email_from
        msg['To'] = email_to
        msg['Subject'] = email_subject

        # 添加邮件正文
        body = "Please find the attached test report."
        msg.attach(MIMEText(body, 'plain'))

        # 添加附件
        attachment = open(file_path, "rb")
        part = MIMEBase('application', 'octet-stream')
        part.set_payload(attachment.read())
        encoders.encode_base64(part)
        part.add_header('Content-Disposition', f"attachment; filename= {os.path.basename(file_path)}")
        msg.attach(part)
        attachment.close()

        # 连接到 SMTP 服务器并发送邮件
        print(f"正在连接到 SMTP 服务器 {smtp_server}:{smtp_port}...")  # 222222222
        with smtplib.SMTP(smtp_server, smtp_port) as server:
            server.starttls()  # 启用TLS加密  # 222222222
            print("正在登录 SMTP 服务器...")
            server.login(email_username, email_password)  # 222222222
            print("登录成功。")
            text = msg.as_string()
            server.sendmail(email_from, email_to, text)  # 222222222
            print(f"邮件已发送至 {email_to}。")
    except smtplib.SMTPAuthenticationError as e:
        print(f"SMTP 身份验证错误: {e}")
    except smtplib.SMTPServerDisconnected as e:
        print(f"SMTP 服务器断开连接: {e}")
        print("可能的原因包括凭证错误或网络问题。")  # 222222222
    except Exception as e:
        print(f"发送邮件时发生错误: {e}")


def main():
    # 获取当前脚本所在目录的绝对路径
    script_dir = os.path.dirname(os.path.abspath(__file__))

    # 创建 reports 目录（如果不存在）
    reports_dir = os.path.join(script_dir, 'reports')
    if not os.path.exists(reports_dir):
        os.makedirs(reports_dir)

    # 生成带有时间戳的 HTML 报告文件名
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")  # 生成时间戳
    report_filename = f"test_report_{timestamp}.html"  # 添加时间戳到文件名
    report_file = os.path.join(reports_dir, report_filename)  # 构建带时间戳的报告文件路径

    # JSON 报告文件路径保持不变
    json_report_file = os.path.join(reports_dir, 'report.json')

    # 运行 pytest 并生成 HTML 报告和 JSON 报告
    pytest_args = [
        '-v',
        '--tb=short',
        '--json-report-file={}'.format(json_report_file),  # 使用 pytest-json-report 生成 JSON 报告，并指定报告文件路径
        'tests/'  # 指定要运行的测试文件路径为 tests 目录
    ]

    print("正在运行 pytest...")
    pytest.main(pytest_args)

    # 检查报告文件是否存在
    if os.path.exists(json_report_file):
        print(f"JSON 报告文件 {json_report_file} 存在。")
    else:
        print(f"JSON 报告文件 {json_report_file} 未找到。")
        return

    # 打印 JSON 文件内容以便调试
    with open(json_report_file, 'r', encoding='utf-8') as file:
        json_content = file.read()
        print("JSON 内容:\n", json_content)

    # 读取 JSON 报告文件
    with open(json_report_file, 'r', encoding='utf-8') as file:
        test_results = json.load(file)

    # 提取测试用例数据
    test_cases = []
    total_tests = 0
    passed_tests = 0

    for test_case in test_results.get('tests', []):  # 直接从 test_results 获取 tests 列表
        nodeid = test_case.get('nodeid', '')
        outcome = test_case.get('outcome', '').lower()  # 将 outcome 转换为小写以统一处理
        longrepr = test_case.get('longrepr', '')

        total_tests += 1

        # 获取测试函数的文档字符串
        print(f"正在获取 nodeid: {nodeid} 的文档字符串")  # 调试输出
        description = get_docstring_from_nodeid(nodeid)  # 调用函数获取文档字符串
        print(f"{nodeid} 的文档字符串: {description}")  # 调试输出

        status = "Passed" if outcome == 'passed' else "Failed"
        if status == "Passed":
            passed_tests += 1

        test_cases.append({
            "id": nodeid,
            "description": description,  # 使用获取到的文档字符串作为描述
            "status": status,
            "comment": longrepr
        })

    # 计算通过率
    pass_rate = (passed_tests / total_tests) * 100 if total_tests > 0 else 0

    # 打印提取到的测试用例列表以便调试
    print("提取的测试用例列表:\n", test_cases)
    print(f"总测试数: {total_tests}, 通过的测试数: {passed_tests}, 通过率: {pass_rate:.2f}%")

    # 生成 HTML 报告
    report_path = generate_html_report(reports_dir, report_filename, test_cases)

    # 检查 HTML 报告文件是否存在
    if os.path.exists(report_path):
        print(f"HTML 报告文件 {report_path} 存在。")
    else:
        print(f"HTML 报告文件 {report_path} 未找到。")
        return

    # 如果报告文件存在，则调用 send_email_with_attachment 函数发送邮件
    send_email_with_attachment(
        file_path=report_path,  # 报告文件路径
        smtp_server=SMTP_SERVER,  # SMTP服务器地址
        smtp_port=SMTP_PORT,  # SMTP服务器端口
        email_from=EMAIL_FROM,  # 发件人邮箱地址
        email_to=EMAIL_TO,  # 收件人邮箱地址
        email_subject=EMAIL_SUBJECT,  # 邮件主题
        email_username=EMAIL_USERNAME,  # 登录SMTP服务器的用户名
        email_password=EMAIL_PASSWORD  # 登录SMTP服务器的密码
    )


if __name__ == "__main__":
    main()